<template>
  <div style="position: relative;">
    <canvas id="c"></canvas>
  </div>
</template>
<script setup lang="ts">
import {onMounted } from "vue";
//编译着色器代码
function createShader(gl: WebGLRenderingContext, type: GLenum, source: string): WebGLShader | null {
  let shader = gl.createShader(type) as WebGLShader;//创建着色器对象
  gl.shaderSource(shader, source);//提供数据源
  gl.compileShader(shader);//编译->生成着色器
  const success = gl.getShaderParameter(shader, gl.COMPILE_STATUS);
  if (success) {
    return shader;
  } else {
    console.log(gl.getShaderInfoLog(shader));
    gl.deleteShader(shader);
    return null;
  }
}
//创建着色器程序
function createProgram(gl: WebGLRenderingContext, vertexShader: WebGLShader, fragmentShader: WebGLShader): WebGLProgram | null {
  const program = gl.createProgram() as WebGLProgram;
  gl.attachShader(program, vertexShader);
  gl.attachShader(program, fragmentShader);
  gl.linkProgram(program);
  const success = gl.getProgramParameter(program, gl.LINK_STATUS);
  if (success) {
    return program;
  }
  gl.deleteProgram(program);
  return null;
}
//响应window尺寸变化
function resize(canvas: HTMLCanvasElement) {
  // 获取浏览器中画布的显示尺寸
  var displayWidth = canvas.clientWidth;
  var displayHeight = canvas.clientHeight;

  // 检尺寸是否相同
  if (canvas.width != displayWidth ||
    canvas.height != displayHeight) {
    // 设置为相同的尺寸
    canvas.width = displayWidth;
    canvas.height = displayHeight;
  }
}
//单位化向量
function normalize(v:Array<number>) {
  var length = Math.sqrt(v[0] * v[0] + v[1] * v[1] + v[2] * v[2]);
  // 确定不会除以 0
  if (length > 0.00001) {
    return [v[0] / length, v[1] / length, v[2] / length];
  } else {
    return [0, 0, 0];
  }
}
//向量相减
function subtractVectors(a:Array<number>, b:Array<number>) {
  return [a[0] - b[0], a[1] - b[1], a[2] - b[2]];
}
//这是计算叉乘的代码
function cross(a:Array<number>, b:Array<number>) {
  return [a[1] * b[2] - a[2] * b[1],
          a[2] * b[0] - a[0] * b[2],
          a[0] * b[1] - a[1] * b[0]];
}
function degToRad(d: number) {
  return d * Math.PI / 180;
}
// 在缓冲存储构成 立方体 的值
function setGeometry(gl: WebGLRenderingContext) {
  //一个面由两个三角形六个顶点组成
  gl.bufferData(
    gl.ARRAY_BUFFER,
    new Float32Array([
      //前面
      0, 0, 0,
      150, 150, 0,
      0, 150, 0,
      150, 0, 0,
      150, 150, 0,
      0, 0, 0,
     
      // 后面
      0, 150, 150,
      0, 0, 150,
      150, 150, 150,
      150, 0, 150,
      150, 150, 150,
      0, 0, 150,

      // 上面
     
      0, 150, 150,
      0, 150, 0,
      150, 150, 150,
      150, 150, 0,
      150, 150, 150,
      0, 150, 0,

      // 下面
      0, 0, 0,
      0, 0, 150,
      150, 0, 150,
      150, 0, 150,
      150, 0, 0,
      0, 0, 0,

      // 左面
      0, 150, 0,
      0, 0, 0,
      0, 150, 150,
      0, 0, 0,
      0, 0, 150,
      0, 150, 150,
      

      // 右面
      150, 150, 0,
      150, 0, 0,
      150, 150, 150,
      150, 0, 150,
      150, 150, 150,
      150, 0, 0,
    ]),
    gl.STATIC_DRAW);
}
// Fill the buffer with colors for the 'F'.
function setColors(gl: WebGLRenderingContext) {
  gl.bufferData(
    gl.ARRAY_BUFFER,
    new Uint8Array([
      70, 70, 120,
      70, 70, 120,
      70, 70, 120,
      70, 70, 120,
      70, 70, 120,
      70, 70, 120,

      200, 70, 120,
      200, 70, 120,
      200, 70, 120,
      200, 70, 120,
      200, 70, 120,
      200, 70, 120,

      200, 120, 120,
      200, 120, 120,
      200, 120, 120,
      200, 120, 120,
      200, 120, 120,
      200, 120, 120,

      120, 70, 200,
      120, 70, 200,
      120, 70, 200,
      120, 70, 200,
      120, 70, 200,
      120, 70, 200,

      60, 70, 200,
      60, 70, 200,
      60, 70, 200,
      60, 70, 200,
      60, 70, 200,
      60, 70, 200,

      80, 150, 200,
      80, 150, 200,
      80, 150, 200,
      80, 150, 200,
      80, 150, 200,
      80, 150, 200,
    ]),
    gl.STATIC_DRAW);
}
//三维矩阵操作工具
let m4 = {
  //相机朝向矩阵
  lookAt: function(cameraPosition:Array<number>, target:Array<number>, up:Array<number>) {
    var zAxis = normalize(
        subtractVectors(cameraPosition, target));
    var xAxis = normalize(cross(up, zAxis));
    var yAxis = normalize(cross(zAxis, xAxis));
 
    return [
       xAxis[0], xAxis[1], xAxis[2], 0,
       yAxis[0], yAxis[1], yAxis[2], 0,
       zAxis[0], zAxis[1], zAxis[2], 0,
       cameraPosition[0],
       cameraPosition[1],
       cameraPosition[2],
       1,
    ];
  },
  //透视矩阵
  perspective: function (fieldOfViewInRadians: number, aspect: number, near: number, far: number) {
    var f = Math.tan(Math.PI * 0.5 - 0.5 * fieldOfViewInRadians);
    var rangeInv = 1.0 / (near - far);

    return [
      f / aspect, 0, 0, 0,
      0, f, 0, 0,
      0, 0, (near + far) * rangeInv, -1,
      0, 0, near * far * rangeInv * 2, 0
    ];
  },
  //视图矩阵，处理了分辨率适配
  projection: function (width: number, height: number, depth: number) {
    // 注意：这个矩阵翻转了 Y 轴，所以 0 在上方
    return [
      2 / width, 0, 0, 0,
      0, -2 / height, 0, 0,
      0, 0, 2 / depth, 0,
      -1, 1, 0, 1,
    ];
  },
  translation: function (tx: number, ty: number, tz: number) {
    return [
      1, 0, 0, 0,
      0, 1, 0, 0,
      0, 0, 1, 0,
      tx, ty, tz, 1,
    ];
  },

  xRotation: function (angleInRadians: number) {
    var c = Math.cos(angleInRadians);
    var s = Math.sin(angleInRadians);

    return [
      1, 0, 0, 0,
      0, c, s, 0,
      0, -s, c, 0,
      0, 0, 0, 1,
    ];
  },

  yRotation: function (angleInRadians: number) {
    var c = Math.cos(angleInRadians);
    var s = Math.sin(angleInRadians);

    return [
      c, 0, -s, 0,
      0, 1, 0, 0,
      s, 0, c, 0,
      0, 0, 0, 1,
    ];
  },

  zRotation: function (angleInRadians: number) {
    var c = Math.cos(angleInRadians);
    var s = Math.sin(angleInRadians);

    return [
      c, s, 0, 0,
      -s, c, 0, 0,
      0, 0, 1, 0,
      0, 0, 0, 1,
    ];
  },
  scaling: function (sx: number, sy: number, sz: number) {
    return [
      sx, 0, 0, 0,
      0, sy, 0, 0,
      0, 0, sz, 0,
      0, 0, 0, 1,
    ];
  },
  translate: function (m: Array<number>, tx: number, ty: number, tz: number) {
    return m4.multiply(m, m4.translation(tx, ty, tz));
  },

  xRotate: function (m: Array<number>, angleInRadians: number) {
    return m4.multiply(m, m4.xRotation(angleInRadians));
  },

  yRotate: function (m: Array<number>, angleInRadians: number) {
    return m4.multiply(m, m4.yRotation(angleInRadians));
  },

  zRotate: function (m: Array<number>, angleInRadians: number) {
    return m4.multiply(m, m4.zRotation(angleInRadians));
  },
  scale: function (m: Array<number>, sx: number, sy: number, sz: number) {
    return m4.multiply(m, m4.scaling(sx, sy, sz));
  },
  //矩阵相乘
  multiply: function (a: Array<number>, b: Array<number>) {
    var a00 = a[0 * 4 + 0];
    var a01 = a[0 * 4 + 1];
    var a02 = a[0 * 4 + 2];
    var a03 = a[0 * 4 + 3];
    var a10 = a[1 * 4 + 0];
    var a11 = a[1 * 4 + 1];
    var a12 = a[1 * 4 + 2];
    var a13 = a[1 * 4 + 3];
    var a20 = a[2 * 4 + 0];
    var a21 = a[2 * 4 + 1];
    var a22 = a[2 * 4 + 2];
    var a23 = a[2 * 4 + 3];
    var a30 = a[3 * 4 + 0];
    var a31 = a[3 * 4 + 1];
    var a32 = a[3 * 4 + 2];
    var a33 = a[3 * 4 + 3];
    var b00 = b[0 * 4 + 0];
    var b01 = b[0 * 4 + 1];
    var b02 = b[0 * 4 + 2];
    var b03 = b[0 * 4 + 3];
    var b10 = b[1 * 4 + 0];
    var b11 = b[1 * 4 + 1];
    var b12 = b[1 * 4 + 2];
    var b13 = b[1 * 4 + 3];
    var b20 = b[2 * 4 + 0];
    var b21 = b[2 * 4 + 1];
    var b22 = b[2 * 4 + 2];
    var b23 = b[2 * 4 + 3];
    var b30 = b[3 * 4 + 0];
    var b31 = b[3 * 4 + 1];
    var b32 = b[3 * 4 + 2];
    var b33 = b[3 * 4 + 3];
    return [
      b00 * a00 + b01 * a10 + b02 * a20 + b03 * a30,
      b00 * a01 + b01 * a11 + b02 * a21 + b03 * a31,
      b00 * a02 + b01 * a12 + b02 * a22 + b03 * a32,
      b00 * a03 + b01 * a13 + b02 * a23 + b03 * a33,
      b10 * a00 + b11 * a10 + b12 * a20 + b13 * a30,
      b10 * a01 + b11 * a11 + b12 * a21 + b13 * a31,
      b10 * a02 + b11 * a12 + b12 * a22 + b13 * a32,
      b10 * a03 + b11 * a13 + b12 * a23 + b13 * a33,
      b20 * a00 + b21 * a10 + b22 * a20 + b23 * a30,
      b20 * a01 + b21 * a11 + b22 * a21 + b23 * a31,
      b20 * a02 + b21 * a12 + b22 * a22 + b23 * a32,
      b20 * a03 + b21 * a13 + b22 * a23 + b23 * a33,
      b30 * a00 + b31 * a10 + b32 * a20 + b33 * a30,
      b30 * a01 + b31 * a11 + b32 * a21 + b33 * a31,
      b30 * a02 + b31 * a12 + b32 * a22 + b33 * a32,
      b30 * a03 + b31 * a13 + b32 * a23 + b33 * a33,
    ];
  },
  orthographic: function (left: number, right: number, bottom: number, top: number, near: number, far: number) {
    return [
      2 / (right - left), 0, 0, 0,
      0, 2 / (top - bottom), 0, 0,
      0, 0, 2 / (near - far), 0,

      (left + right) / (left - right),
      (bottom + top) / (bottom - top),
      (near + far) / (near - far),
      1,
    ];
  },
  //逆矩阵
  inverse: function(m:Array<number>) {
    var m00 = m[0 * 4 + 0];
    var m01 = m[0 * 4 + 1];
    var m02 = m[0 * 4 + 2];
    var m03 = m[0 * 4 + 3];
    var m10 = m[1 * 4 + 0];
    var m11 = m[1 * 4 + 1];
    var m12 = m[1 * 4 + 2];
    var m13 = m[1 * 4 + 3];
    var m20 = m[2 * 4 + 0];
    var m21 = m[2 * 4 + 1];
    var m22 = m[2 * 4 + 2];
    var m23 = m[2 * 4 + 3];
    var m30 = m[3 * 4 + 0];
    var m31 = m[3 * 4 + 1];
    var m32 = m[3 * 4 + 2];
    var m33 = m[3 * 4 + 3];
    var tmp_0  = m22 * m33;
    var tmp_1  = m32 * m23;
    var tmp_2  = m12 * m33;
    var tmp_3  = m32 * m13;
    var tmp_4  = m12 * m23;
    var tmp_5  = m22 * m13;
    var tmp_6  = m02 * m33;
    var tmp_7  = m32 * m03;
    var tmp_8  = m02 * m23;
    var tmp_9  = m22 * m03;
    var tmp_10 = m02 * m13;
    var tmp_11 = m12 * m03;
    var tmp_12 = m20 * m31;
    var tmp_13 = m30 * m21;
    var tmp_14 = m10 * m31;
    var tmp_15 = m30 * m11;
    var tmp_16 = m10 * m21;
    var tmp_17 = m20 * m11;
    var tmp_18 = m00 * m31;
    var tmp_19 = m30 * m01;
    var tmp_20 = m00 * m21;
    var tmp_21 = m20 * m01;
    var tmp_22 = m00 * m11;
    var tmp_23 = m10 * m01;

    var t0 = (tmp_0 * m11 + tmp_3 * m21 + tmp_4 * m31) -
        (tmp_1 * m11 + tmp_2 * m21 + tmp_5 * m31);
    var t1 = (tmp_1 * m01 + tmp_6 * m21 + tmp_9 * m31) -
        (tmp_0 * m01 + tmp_7 * m21 + tmp_8 * m31);
    var t2 = (tmp_2 * m01 + tmp_7 * m11 + tmp_10 * m31) -
        (tmp_3 * m01 + tmp_6 * m11 + tmp_11 * m31);
    var t3 = (tmp_5 * m01 + tmp_8 * m11 + tmp_11 * m21) -
        (tmp_4 * m01 + tmp_9 * m11 + tmp_10 * m21);

    var d = 1.0 / (m00 * t0 + m10 * t1 + m20 * t2 + m30 * t3);

    return [
      d * t0,
      d * t1,
      d * t2,
      d * t3,
      d * ((tmp_1 * m10 + tmp_2 * m20 + tmp_5 * m30) -
            (tmp_0 * m10 + tmp_3 * m20 + tmp_4 * m30)),
      d * ((tmp_0 * m00 + tmp_7 * m20 + tmp_8 * m30) -
            (tmp_1 * m00 + tmp_6 * m20 + tmp_9 * m30)),
      d * ((tmp_3 * m00 + tmp_6 * m10 + tmp_11 * m30) -
            (tmp_2 * m00 + tmp_7 * m10 + tmp_10 * m30)),
      d * ((tmp_4 * m00 + tmp_9 * m10 + tmp_10 * m20) -
            (tmp_5 * m00 + tmp_8 * m10 + tmp_11 * m20)),
      d * ((tmp_12 * m13 + tmp_15 * m23 + tmp_16 * m33) -
            (tmp_13 * m13 + tmp_14 * m23 + tmp_17 * m33)),
      d * ((tmp_13 * m03 + tmp_18 * m23 + tmp_21 * m33) -
            (tmp_12 * m03 + tmp_19 * m23 + tmp_20 * m33)),
      d * ((tmp_14 * m03 + tmp_19 * m13 + tmp_22 * m33) -
            (tmp_15 * m03 + tmp_18 * m13 + tmp_23 * m33)),
      d * ((tmp_17 * m03 + tmp_20 * m13 + tmp_23 * m23) -
            (tmp_16 * m03 + tmp_21 * m13 + tmp_22 * m23)),
      d * ((tmp_14 * m22 + tmp_17 * m32 + tmp_13 * m12) -
            (tmp_16 * m32 + tmp_12 * m12 + tmp_15 * m22)),
      d * ((tmp_20 * m32 + tmp_12 * m02 + tmp_19 * m22) -
            (tmp_18 * m22 + tmp_21 * m32 + tmp_13 * m02)),
      d * ((tmp_18 * m12 + tmp_23 * m32 + tmp_15 * m02) -
            (tmp_22 * m32 + tmp_14 * m02 + tmp_19 * m12)),
      d * ((tmp_22 * m22 + tmp_16 * m02 + tmp_21 * m12) -
            (tmp_20 * m12 + tmp_23 * m22 + tmp_17 * m02))
    ];
  },
}
const color = [Math.random(), Math.random(), Math.random(), 1];
//属性和全局变量
let positionAttributeLocation: number;
let colorLocation: number;
let matrixLocation: WebGLUniformLocation;
//位置顶点缓冲
let positionBuffer: WebGLBuffer;
var colorBuffer: WebGLBuffer;
//着色器程序对象
let program: WebGLProgram;
let gl: WebGLRenderingContext;
let fieldOfViewRadians = degToRad(60);
onMounted(() => {
  /* 初始化工作开始-------- */
  const canvas = document.getElementById("c") as HTMLCanvasElement;
  gl = canvas.getContext("webgl") as WebGLRenderingContext;
  if (!gl) {
    console.log('你不能使用WebGL');
    return;
  }
  //创建着色器
  const vertexShaderSource = document.querySelector("#vertex-shader-2d")?.textContent as string;
  const fragmentShaderSource = document.querySelector("#fragment-shader-2d")?.textContent as string;
  const vertexShader = createShader(gl, gl.VERTEX_SHADER, vertexShaderSource) as WebGLShader;
  const fragmentShader = createShader(gl, gl.FRAGMENT_SHADER, fragmentShaderSource) as WebGLShader;

  //将着色器链接到一个program(着色器程序)
  program = createProgram(gl, vertexShader, fragmentShader) as WebGLProgram;
  //找到顶点着色器中属性值所在的位置
  positionAttributeLocation = gl.getAttribLocation(program, 'a_position');
  colorLocation = gl.getAttribLocation(program, "a_color");
  matrixLocation = gl.getUniformLocation(program, 'u_matrix') as WebGLUniformLocation;
  console.log(matrixLocation);
  
  //属性值从缓冲中获取数据，创建一个缓冲
  positionBuffer = gl.createBuffer() as WebGLBuffer;
  // 绑定位置缓冲
  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
  // 设置图形顶点数据
  setGeometry(gl);//bufferData
  colorBuffer = gl.createBuffer() as WebGLBuffer;
  gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
  setColors(gl);
  /* 初始化工作结束------- */
  /* 开始渲染------- */
  drawScence(gl)
});
var radius = 200;
//绘制场景
function drawScence(gl: WebGLRenderingContext) {
  cameraAngleRadians+= rotationSpeed;  // 
  resize(gl.canvas);
  //这样就告诉WebGL裁剪空间的 -1 -> +1 分别对应到x轴的 0 -> gl.canvas.width 和y轴的 0 -> gl.canvas.height。
  gl.viewport(0, 0, gl.canvas.width, gl.canvas.height);
  //清空画布
  gl.clearColor(0, 0, 0, 0);
  gl.clear(gl.COLOR_BUFFER_BIT | gl.DEPTH_BUFFER_BIT);
  //开启这个特性后WebGL默认“剔除”背面三角形
  // gl.enable(gl.CULL_FACE);
  //启用深度缓冲
  gl.enable(gl.DEPTH_TEST);
  // 告诉它用我们之前写好的着色程序（一个着色器对）
  gl.useProgram(program);
  //启用属性
  gl.enableVertexAttribArray(positionAttributeLocation);
  gl.bindBuffer(gl.ARRAY_BUFFER, positionBuffer);
  //WebGL可以通过绑定点操控全局范围内的许多数据，你可以把绑定点想象成一个WebGL内部的全局变量。 首先绑定一个数据源到绑定点，然后可以引用绑定点指向该数据源。 所以让我们来绑定位置信息缓冲（下面的绑定点就是ARRAY_BUFFER）。
  let size = 3;// 每次迭代运行提取3个单位数据
  let type = gl.FLOAT;// 每个单位的数据类型是32位浮点型
  let normalize = false;// 不需要归一化数据
  let stride = 0;// 0 = 移动单位数量 * 每个单位占用内存（sizeof(type)）
  // 每次迭代运行运动多少内存到下一个数据开始点
  let offset = 0;  // 从缓冲起始位置开始读取
  gl.vertexAttribPointer(positionAttributeLocation, size, type, normalize, stride, offset);

  // 启用颜色属性
  gl.enableVertexAttribArray(colorLocation);
  // Bind the color buffer.
  gl.bindBuffer(gl.ARRAY_BUFFER, colorBuffer);
  var size1 = 3;                 // 3 components per iteration
  var type1 = gl.UNSIGNED_BYTE;  // the data is 8bit unsigned values
  var normalize1 = true;         // normalize the data (convert from 0-255 to 0-1)
  var stride1 = 0;               // 0 = move forward size * sizeof(type) each iteration to get the next position
  var offset1 = 0;               // start at the beginning of the buffer
  gl.vertexAttribPointer(colorLocation, size1, type1, normalize1, stride1, offset1);
  /* 矩阵计算开始 */
  //计算投影矩阵
  var aspect = gl.canvas.width / gl.canvas.height;
  var zNear = 0.1;
  var zFar = 1500;
   // 计算立方体中心的位置
  var fPosition = [75, 75, 75];
  var projectionMatrix = m4.perspective(fieldOfViewRadians, aspect, zNear, zFar);
  // 计算相机的矩阵
  //相机绕Y轴旋转矩阵
  // var cameraMatrix = m4.yRotation(cameraAngleRadians);
  //平移
  var cameraMatrix = m4.translation(200, 400, radius*2);
  // 获得矩阵中相机的位置
  var cameraPosition = [
    cameraMatrix[12],
    cameraMatrix[13],
    cameraMatrix[14],
  ];
  var up = [0, 1, 0];
  // 计算相机的朝向矩阵
  cameraMatrix = m4.lookAt(cameraPosition, fPosition, up);
  // 通过相机矩阵计算视图矩阵，利用相对论的概念，视图的运动是相机的反方向运动，因此求出逆矩阵就可
  var viewMatrix = m4.inverse(cameraMatrix);
  // 计算组合矩阵
  var matrix = m4.multiply(projectionMatrix, viewMatrix);
  matrix=m4.yRotate(matrix,cameraAngleRadians)
  matrix=m4.zRotate(matrix,cameraAngleRadians)
  matrix=m4.translate(matrix,-75,-75,-75)
  /* 矩阵计算结束 */

  // 设置矩阵
  gl.uniformMatrix4fv(matrixLocation, false, matrix);
  // 绘制图形
  gl.drawArrays(gl.TRIANGLES, 0, 6*6);
  requestAnimationFrame(()=>{drawScence(gl)});
}
let cameraAngleRadians=0;
var rotationSpeed = 0.01;
</script>

<style scoped>
canvas {
  width: 100vw;
  height: 100vh;
  display: block;
}
</style>
